package 线程通信;

public class 阻塞队列 {

    //使用数组实现一个循环队列
    private Runnable[] tasks;

    //容量：存放的元素数量
    //多线程存取元素时，要根据已存放数量来判断，是否可以存取
    private int count;

    //存放元素的位置
    private int putIndex;
    //取元素的位置
    private int takeIndex;

    public 阻塞队列(int size) {
        tasks = new Runnable[size];
    }

    //存放元素：线程安全的
    public void put(Runnable task){
        try {
            synchronized (阻塞队列.class){
                //存放的数组满了，就需要等待
                while (count == tasks.length){
                    阻塞队列.class.wait();
                }
                //放到数组中
                tasks[putIndex] = task;
                //更新索引
                putIndex = (putIndex+1) % tasks.length;
                //更新存放数量
                count++;
                //通知
                阻塞队列.class.notifyAll();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public Runnable take(){
        try {
            synchronized (阻塞队列.class){
                //队列空，等待
                while (count == 0){
                    阻塞队列.class.wait();
                }
                //取元素
                Runnable task = tasks[takeIndex];
                //更新索引
                takeIndex = (takeIndex+1) % tasks.length;
                //更新存放数量
                count--;
                阻塞队列.class.notifyAll();
                return task;
            }
        } catch (InterruptedException e) {
            throw new RuntimeException("存放元素出错", e);
        }
    }

    //测试一下
    public static void main(String[] args) {
        Runnable consumer = new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+" 消费了");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        阻塞队列 blockingQueue = new 阻塞队列(20);
        for(int i=0; i<5; i++){
            //模拟生产者：生产了5个任务
            blockingQueue.put(consumer);//main线程生产
        }
        //模拟消费者：不停的消费任务
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    Runnable task = blockingQueue.take();
                    task.run();
                }
            }
        }, "消费者").start();
    }
}